package org.zn.note.jdk.jvm;

import java.util.ArrayList;
import java.util.List;

/**
 * 一个Java进程的内存分配：（比如32位windows一个进程最多2个G）
 * 1、虚拟机本身的内存（先忽略）
 * 2、最大堆内存（-Xmx）
 * 3、最大方法区（-MaxPermSize）（HotSpot永久代）  --- 1.8以后没有方法区了，新增“元数据区”，不算JVM运行时数据区，在进程的内存里，和“直接内存”一样
 * 4、程序计数器（很小，忽略）
 * 5、虚拟机栈和本地方法栈（当虚拟机栈内存一定时，每个线程用的虚拟机栈越大，则能用的线程越少）
 * 6、直接内存（-XX:MaxDirectMemorySize，默认和Xmx一样大）（上面那些内存都是归JVM管的，是JVM运行时内存，而这个不是，是JVM这个进程的内存，“本地内存”）
 * （直接内存溢出不会再HeapDump文件里，所以如果内存溢出，dump又很小，可以考虑是直接内存溢出）
 * <p>
 * 字符串常量：1.7之前在方法区，1.7之后在Heap里
 * <p>
 * 对象已死？
 * 1、应用计数法 Reference Counting （主流JVM没用，有循环引用问题）
 * 2、可达性分析 Reachability Analysis
 * ① 从一系列GC Root往下走，走过的路径叫引用链Reference Chain，当一个对象“不可达”时，就是可回收
 * ② 如何选取GC Root：A、虚拟机栈(本地变量表)中引用的对象 B、(方法区)类静态属性、常量引用的对象 C、本地方法区引用的对象
 * （总结下来就是，当前执行的[虚拟机栈+本地方法栈]引用的对象，和全局(方法区)引用的对象）
 * ③ 关于引用：
 * A、强引用
 * B、软引用(SoftReference)，将要OOM时回收
 * C、弱引用(WeakReference)，只能活到下次GC
 * D、虚引用(PhantomReference)，仅仅为了在GC时能收到一个通知
 * <p>
 * 垃圾收集算法
 * 1、标记清除  效率不高、产生碎片
 * 2、复制  内存分成两半，如S1、S2，S1中存活的转移到S2，然后直接清空S1。新生代都是这样的 Eden、S0、S1
 * 3、标记-整理 标记，整理(把有用的放一边)，清除(直接清除另一边)
 */
public class JvmMain {

    // OOM:OutOfMemory
    static class OOMObject {
    }

    // 测试堆溢出
    // -Xmx20M -Xms20M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/  （指定dump文件位置）
    // java.lang.OutOfMemoryError: Java heap space
    // Dumping heap to java_pid9320.hprof ...
    private static void testHeapOutOfMemory() {
        List<OOMObject> list = new ArrayList<>();
        while (true)
            list.add(new OOMObject());
    }

    // 测试栈溢出
    // -Xss128K -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/
    private static int stackLength = 1;

    private static void stackLeak() { // 递归测试栈深度
        stackLength++;
        stackLeak();
    }

    private static void testStackLeak() {
        try {
            stackLeak();
        } catch (Throwable e) {
            System.out.println("Stack Length: " + stackLength);
            e.printStackTrace();
        }
    }

    // 递归会栈溢出，看看循环会不会
    private static void testFor() {
        for(int i=0; i<1000000; i++)
            stackLength++;
    }

    public static void main(String[] args) {
//        testHeapOutOfMemory(); // 测试Heap Out Of Memory
//        testStackLeak();
        testFor();
    }
}
